﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Net.NetworkInformation;
using System.Net;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace UpdateManager {
    /// <summary>
    /// 下载管理
    /// </summary>
    public class DownloadManager {

        #region 事件

        /// <summary>
        /// 下载百分比事件
        /// </summary>
        public event EventHandler<ProgressEventArgs> DownloadProgress;
        /// <summary>
        /// 下载完成事件
        /// </summary>
        public event EventHandler<EventArgs> DownloadComplete;
        /// <summary>
        /// 下载出错事件
        /// </summary>
        public event EventHandler<ErrorEventArgs> DownloadError;

        /// <summary>
        /// 执行百分比事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDownloadProgress(object sender, ProgressEventArgs e) {
            if (this.DownloadProgress != null) {
                this.DownloadProgress(sender, e);
            }
        }

        /// <summary>
        /// 执行下载完成事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDownloadComplete(object sender, EventArgs e) {
            if (this.DownloadComplete != null) {
                this.DownloadComplete(sender, e);
            }
        }

        /// <summary>
        /// 执行下载暂停事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDownloadPause(object sender, EventArgs e) {
            if (this.DownloadComplete != null) {
                this.DownloadComplete(sender, e);
            }
        }

        /// <summary>
        /// 执行下载出错事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDownloadError(object sender, ErrorEventArgs e) {
            if (this.DownloadError != null) {
                this.DownloadError(sender, e);
            }
        }

        #endregion

        /// <summary>
        /// 下载线程
        /// </summary>
        private Thread threadDownload = null;
        /// <summary>
        /// 是否正在下载
        /// </summary>
        private bool isDownloading = false;

        /// <summary>
        /// 存储文件夹
        /// </summary>
        private string saveFolder = String.Empty;
        /// <summary>
        /// 下载地址
        /// </summary>
        private string downloadUrl = String.Empty;

        /// <summary>
        /// 用来计算下载速度的计时器
        /// </summary>
        private Timer trSpeed = null;
        /// <summary>
        /// 上次计算速度时的下载量
        /// </summary>
        private long lastSize = 0;
        /// <summary>
        /// 当前下载量
        /// </summary>
        private long currentSize = 0;
        /// <summary>
        /// 文件大小
        /// </summary>
        private long fileSize = 0;
        /// <summary>
        /// 总用时（秒）
        /// </summary>
        private int totalSeconds = 0;

        /// <summary>
        /// 创建一个<see cref="DownloadManager"/>实例
        /// </summary>
        public DownloadManager() {
            this.threadDownload = new Thread(Download);
            this.threadDownload.IsBackground = true;
        }

        /// <summary>
        /// 创建一个<see cref="DownloadManager"/>实例
        /// </summary>
        /// <param name="saveFolder">存储文件夹</param>
        /// <param name="downloadUrl">下载地址</param>
        public DownloadManager(string saveFolder, string downloadUrl)
            : this() {
            this.saveFolder = saveFolder;
            this.downloadUrl = downloadUrl;
        }

        /// <summary>
        /// 下载方法
        /// </summary>
        private void Download() {
            //判断是否设置了下载文件夹
            if (this.saveFolder != null) {
                if (!Directory.Exists(this.saveFolder)) {
                    Directory.CreateDirectory(this.saveFolder);
                }
            }

            try {
                //判断是否设置了下载地址
                if (!String.IsNullOrEmpty(this.downloadUrl)) {
                    //根据下载地址获取文件名
                    string fileName = GetFileName(this.downloadUrl);

                    //初始化WebRequest对象
                    WebRequest httpRequest = WebRequest.Create(this.downloadUrl);
                    //初始化WebResponse请求对象
                    WebResponse httpResponse = httpRequest.GetResponse();

                    //获取当前文件长度
                    this.fileSize = httpResponse.ContentLength;
                    //初始化数据流对象
                    Stream stream = httpResponse.GetResponseStream();

                    //初始化文件保存地址
                    string filePath = this.saveFolder == null ? fileName : this.saveFolder + "\\" + fileName;
                    //初始化文件读写流对象
                    FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);

                    //声明读取长度
                    int length;
                    ////初始化下载长度(大小)
                    //int downLength = 0;

                    byte[] buffer = new byte[4096];

                    Stopwatch swTotal = new Stopwatch();
                    swTotal.Start();

                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    
                    //循环, 判断文件是否已经下载完成
                    while ((length = stream.Read(buffer, 0, buffer.Length)) > 0) {

                        sw.Stop();

                        //记录已经下载的长度(大小)
                        this.currentSize += length;

                        //将缓存写入文件
                        fs.Write(buffer, 0, length);

                        sw.Start();
                    }
                    sw.Stop();
                    swTotal.Stop();

                    //计算下载速度
                    string avgSpead = (this.fileSize / this.totalSeconds).ToString("0.00") + " KB/S";

                    //触发下载百分比事件
                    this.OnDownloadProgress(this, new ProgressEventArgs() { Progress = 100, Spead = avgSpead });
                    
                    fs.Flush();
                    fs.Close();
                    fs = null;
                } else {
                    //触发下载出错事件
                    this.OnDownloadError(this, new ErrorEventArgs() { ErrorMessage = "下载地址不正确" });
                }
            } catch (Exception ex) {
                //触发下载出错事件
                this.OnDownloadError(this, new ErrorEventArgs() { ErrorMessage = "未知错误：" + ex.Message });
            }

            this.isDownloading = false;
            this.trSpeed = null;

            //触发下载完成事件 
            this.OnDownloadComplete(this, null);
        }

        /// <summary>
        /// 计时回调方法
        /// </summary>
        private void SetSpeed(object obj) {
            if (this.isDownloading) {
                //计算下载速度
                string speed = ((this.currentSize - this.lastSize) / 1024.0).ToString("0.0") + " KB/S";
                //计算百分比
                int progress = (int)(100.0 * this.currentSize / this.fileSize);

                //触发下载百分比事件
                this.OnDownloadProgress(this, new ProgressEventArgs() { Progress = progress, Spead = speed });

                //记录已经下载的长度(大小)
                this.lastSize = this.currentSize;

                this.totalSeconds++;
            }
        }

        /// <summary>
        /// 获取文件名
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetFileName(string url) {
            string[] split = url.Split('/');

            if (split.Length > 0) {
                return split[split.Length - 1];
            } else {
                return "";
            }
        }

        /// <summary>
        /// 开始下载
        /// </summary>
        public void DoStart() {
            this.lastSize = 0;
            this.currentSize = 0;

            if (this.trSpeed == null) {
                this.trSpeed = new Timer(new TimerCallback(SetSpeed), null, new TimeSpan(0, 0, 1), new TimeSpan(0, 0, 1));                 
            }

            this.isDownloading = true;

            if ((this.threadDownload.ThreadState & System.Threading.ThreadState.Suspended) > 0 ) {
                this.threadDownload.Resume();
            } else {
                this.threadDownload = new Thread(Download);
                this.threadDownload.IsBackground = true;
                this.threadDownload.Start();
            }
        }

        /// <summary>
        /// 暂停下载
        /// </summary>
        public void DoPause() {
            this.isDownloading = false;

            if ((this.threadDownload.ThreadState & System.Threading.ThreadState.Unstarted) == 0) {
                this.threadDownload.Suspend();
            }
        }

        /// <summary>
        /// 停止下载
        /// </summary>
        public void DoStop() {
            this.isDownloading = false;
            if ((this.threadDownload.ThreadState & System.Threading.ThreadState.Suspended) == 0) {
                this.threadDownload.Abort();
            }
        }

        #region 获取网络连接状态

        /// <summary>
        /// 声明获取网络连接状态的API函数
        /// </summary>
        /// <param name="connectionDescription">连接说明</param>
        /// <param name="reservedValue">保留值</param>
        /// <returns>是否连接到Internet</returns>
        [DllImport("wininet.dll")]
        private extern static bool InternetGetConnectedState(out int connectionDescription, int reservedValue);

        /// <summary>
        /// 获取网络连接状态
        /// </summary>
        /// <returns></returns>
        public static bool GetInternetConnectedState() {
            //声明连接说明变量
            int connectionDescription = 0;

            //调用API函数, 获取连接状态
            bool state = InternetGetConnectedState(out connectionDescription, 0);

            //返回连接状态
            return state;
        }

        /// <summary>
        /// Ping指定的IP地址, 并获取Ping状态
        /// </summary>
        /// <param name="ipAddress">IP地址</param>
        /// <returns>是否能PI通</returns>
        public static bool PingIpAddress(string ipString) {
            //初始化Ping对象
            Ping ping = new Ping();

            //声明IPAddress对象
            IPAddress ipAddress = null;

            //从IP地址字符串中获取IPAddress对象
            IPAddress.TryParse(ipString, out ipAddress);

            //判断IP地址是否为Null
            if (ipAddress != null) {
                //获取Ping返回值
                PingReply reply = ping.Send(ipAddress);

                //返回是否Ping通
                return reply.Status == IPStatus.Success;
            }

            //返回没有Ping通
            return false;
        }

        #endregion

        #region 属性

        /// <summary>
        /// 获取或设置存储文件夹
        /// </summary>
        public string SaveFolder {
            get { return saveFolder; }
            set { saveFolder = value; }
        }

        /// <summary>
        /// 获取或设置下载地址
        /// </summary>
        public string DownloadUrl {
            get { return downloadUrl; }
            set { downloadUrl = value; }
        }

        /// <summary>
        /// 获取是否正在下载
        /// </summary>
        public bool IsDownloading {
            get { return isDownloading; }
        }

        #endregion
    }
}
